package gin

import (
	"errors"
	"reflect"
	"strings"
)

// Validate 校验必须字段通过结构体标签辅助校验
func Validate(c *Context, obj any) error {
	var err error
	//typ := refl
	// 通过反射获取要校验的数据类型和值
	typ := reflect.TypeOf(obj)
	val := reflect.ValueOf(obj)
	// 检查obj是否为指针类型,如果是就获取指定所有执行的值和类型
	if typ.Kind() == reflect.Ptr {
		// 返回指针所指向的类型
		typ = typ.Elem()
		// 返回指针所指向的值
		val = val.Elem()
	}

	// 遍历结构体每个字段
	for i := 0; i < typ.NumField(); i++ {
		// 获取索引为i的字段信息(包括名称，标签，类型等信息)
		field := typ.Field(i)
		// 将获取结构体中的值并断言转换为interface{}类型
		fieldValue := val.Field(i).Interface()

		// 创建结构体中每种类型的零值，并将其转换成interface{}
		zero := reflect.Zero(field.Type).Interface()

		// 检查字段是否为结构体类型或者非空指针所指向的结构体类型
		// 指针不为nil
		if field.Type.Kind() == reflect.Struct || (field.Type.Kind() == reflect.Ptr && !reflect.DeepEqual(zero, fieldValue)) {
			// 满足条件,递归调用(主要防止结构体嵌套)
			err = Validate(c, fieldValue)
		}

		// 查找required字符串在binding标签值中的位置,只要不为-1则找到了
		if strings.Index(field.Tag.Get("binding"), "required") > -1 {
			// 判断字段值是否为零值
			if reflect.DeepEqual(zero, fieldValue) {
				// 如果字段值是零值且存在required标签,则表示该字段是必须字段但未提供值
				name := field.Name

				// 检查字段标签是否存在json和form,存在则使用其值作为字段名称
				if j := field.Tag.Get("json"); j != "" {
					name = j
				} else if f := field.Tag.Get("form"); f != "" {
					name = f
				}
				err = errors.New("Required " + name)
				// 将错误信息添加至错误列表
				c.Error(err, "json validation")
			}
		}
	}
	return err
}
